package com.jwds.digital.bios.business.service.service.belt.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jwds.digital.bios.business.core.infrastructure.dto.belt.FiveDefenseCount;
import com.jwds.digital.bios.business.core.infrastructure.dto.belt.assembler.BeltAnomalyInfoAssembler;
import com.jwds.digital.bios.business.core.infrastructure.entity.belt.*;
import com.jwds.digital.bios.business.service.service.belt.IBeltAnomalyInfoService;
import com.jwds.digital.bios.business.service.service.belt.IBeltHealthConfigService;
import com.jwds.digital.bios.business.service.service.belt.IBeltHealthScoreDayService;
import com.jwds.digital.bios.business.service.service.belt.IBeltHealthScoreService;
import com.jwds.digital.bios.business.core.infrastructure.dto.belt.BeltHealthScoreReqDTO;
import com.jwds.digital.bios.business.core.infrastructure.vo.belt.BeltHealthScoreRespVO;
import com.jwds.digital.bios.business.core.infrastructure.dto.belt.assembler.BeltHealthScoreAssembler;
import com.jwds.digital.bios.business.core.infrastructure.mapper.belt.BeltHealthScoreMapper;
import com.jwds.digital.bios.business.core.exceptions.RecordNotFoundException;
import com.jwds.tdc.framework.application.lib.page.PageRequest;
import com.jwds.tdc.framework.application.lib.page.PageResponse;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;

import com.jwds.tdc.framework.cache.service.CacheService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

import static com.jwds.digital.bios.business.service.service.belt.IBeltAnomalyInfoService.*;

/**
 * 皮带健康评分表实现类
 *
 * @author 林柏云
 * @date 2024-10-24
 */
@Slf4j
@Service
public class BeltHealthScoreServiceImpl extends ServiceImpl<BeltHealthScoreMapper, BeltHealthScore> implements IBeltHealthScoreService {

    @Autowired
    private BeltHealthScoreMapper baseMapper;

    @Resource
    IBeltAnomalyInfoService beltAnomalyInfoService;

    @Resource
    IBeltHealthScoreDayService beltHealthScoreDay;

    @Autowired
    IBeltHealthConfigService beltHealthConfigService;

    @Resource
    CacheService cacheService;

    @Override
    public BeltHealthScoreRespVO queryById(Long id) {
        if (id == null) {
            return null;
        }
        BeltHealthScore entity = baseMapper.selectById(id);
        return BeltHealthScoreAssembler.entity2dto(entity);
    }

    @Override
    public PageResponse<BeltHealthScoreRespVO> page(PageRequest<BeltHealthScoreReqDTO> request) {
        BeltHealthScoreReqDTO condition = request.getQueryCondition();
        Page<BeltHealthScoreRespVO> page = new Page<>(request.getPageIndex(), request.getPageSize());
        IPage<BeltHealthScoreRespVO> pageData = baseMapper.pageQuery(page, condition);
        return new PageResponse<>((Page<?>) pageData, pageData.getRecords());
    }

    @Override
    public List<BeltHealthScoreRespVO> list(BeltHealthScoreReqDTO reqDto) {
        return baseMapper.queryList(reqDto);
    }

    @Override
    public void add(BeltHealthScoreReqDTO reqDto) {
        BeltHealthScore entity = BeltHealthScoreAssembler.dto2entity(reqDto);
        baseMapper.insert(entity);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchAdd(List<BeltHealthScoreReqDTO> dtoList) {
        dtoList.forEach(this::add);
    }

    @Override
    public void update(BeltHealthScoreReqDTO reqDto) {
        BeltHealthScore oldEntity = baseMapper.selectById(reqDto.getId());
        if (oldEntity == null) {
            throw new RecordNotFoundException();
        }
        BeltHealthScore newEntity = BeltHealthScoreAssembler.dto2entity(reqDto, oldEntity);
        baseMapper.updateById(newEntity);
    }

    @Override
    public void delete(Long id) {
        BeltHealthScore oldEntity = baseMapper.selectById(id);
        if (oldEntity == null) {
            throw new RecordNotFoundException();
        }
        baseMapper.deleteById(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchDelete(List<Long> ids) {
        for (Long id : ids) {
            delete(id);
        }
    }

    /**
     * 判断数值范围类型异常
     *
     * @param beltPerformanceMinute 皮带性能分钟
     * @param type                  类型
     */
    @Override
    @Transactional
    public void determineTheNumericalRangeTypeAnomaly(BeltPerformanceMinute beltPerformanceMinute, String type) {
        // 判断缓存中是否存在异常
        if (isCacheContainsAnomaly(beltPerformanceMinute, type)) {
            return;
        }

        // 获取配置
        BeltHealthConfig config = getBeltHealthConfig(beltPerformanceMinute, type);

        if (config != null) {
            // 设置缓存
            setCacheForAnomaly(beltPerformanceMinute, type);

            // 创建异常信息和健康分数
            BeltAnomalyInfo anomalyInfo = createBeltAnomalyInfo(beltPerformanceMinute, config);
            BeltHealthScore beltHealthScore = createBeltHealthScore(beltPerformanceMinute, config);

            // 保存异常信息和健康分数
            beltAnomalyInfoService.save(anomalyInfo);
            this.save(beltHealthScore);

            // 更新当天健康分数
            updateBeltHealthScoreDay(beltHealthScore, beltPerformanceMinute.getBeltId());
        }
    }

    @Override
    public void determineTheSignalTypeAnomaly(FiveDefenseCount fiveDefenseCount, String type) {
        Integer count = getFiveDefenseCount(fiveDefenseCount, type);
        if (count==null||count<=0){
            return;
        }
        BeltHealthConfig config = beltHealthConfigService.getOne(new LambdaQueryWrapper<BeltHealthConfig>()
                .eq(BeltHealthConfig::getDeductionCode, type)
                .lt(BeltHealthConfig::getDeductionRangeMin, count)
                .orderByDesc(BeltHealthConfig::getDeductionRangeMin)
                .last("limit 1")
        );
        if (config != null) {
            // 创建异常信息和健康分数
            BeltAnomalyInfo anomalyInfo = createBeltAnomalyInfo(fiveDefenseCount, config);
            BeltHealthScore beltHealthScore = createBeltHealthScore(fiveDefenseCount, config);
            beltAnomalyInfoService.save(anomalyInfo);
            this.save(beltHealthScore);
            // 更新当天健康分数
            updateBeltHealthScoreDay(beltHealthScore, fiveDefenseCount.getBeltId());
        }

    }

    private BeltHealthScore createBeltHealthScore(FiveDefenseCount fiveDefenseCount, BeltHealthConfig config) {
        BeltHealthScore beltHealthScore = new BeltHealthScore();
        beltHealthScore
                .setBeltId(fiveDefenseCount.getBeltId())
                .setDeductionItem(config.getDeductionItem())
                .setDeductionScore(config.getDeductionScore())
                .setHealthType(config.getHealthType())
                .setScoreTime(LocalDateTime.now());
        beltHealthScore.setIsDeleted(0);
        return beltHealthScore;
    }

    private BeltAnomalyInfo createBeltAnomalyInfo(FiveDefenseCount fiveDefenseCount, BeltHealthConfig config) {
        BeltAnomalyInfo anomalyInfo = new BeltAnomalyInfo();
        anomalyInfo
                .setBeltId(fiveDefenseCount.getBeltId())
                .setAnomalyItem(config.getDeductionItem())
                .setItemLevel(config.getAnomalyLevel())
                .setAnomalyDesc(config.getDeductionSuggestion())
                .setSuggestion(config.getDeductionSuggestion())
                .setAnomalyTime(LocalDateTime.now());
        anomalyInfo.setIsDeleted(0);
        return anomalyInfo;
    }

    //通过异常类型返回五防类型计数
    private Integer getFiveDefenseCount(FiveDefenseCount fiveDefenseCount, String type) {
        switch (type){
            case TEAR_CODE: return fiveDefenseCount.getTearCount();
            case SLIP_CODE: return fiveDefenseCount.getSlipCount();
            case ROPE_CODE: return fiveDefenseCount.getRopeCount();
            case BLOCK_CODE: return fiveDefenseCount.getBlockCount();
            case MINOR_DEVIATION_CODE: return fiveDefenseCount.getMinorDeviationCount();
            case SEVERE_DEVIATION_CODE: return fiveDefenseCount.getSevereDeviationCount();
            default: return null;
        }

    }

    // 判断缓存中是否存在异常
    private boolean isCacheContainsAnomaly(BeltPerformanceMinute beltPerformanceMinute, String type) {
        String cacheKey = getCacheKey(type, beltPerformanceMinute);
        return cacheService.getString(cacheKey) != null;
    }

    // 获取BeltHealthConfig
    private BeltHealthConfig getBeltHealthConfig(BeltPerformanceMinute beltPerformanceMinute, String type) {
        return beltHealthConfigService.getOne(
                new LambdaQueryWrapper<BeltHealthConfig>()
                        .lt(BeltHealthConfig::getDeductionRangeMin, beltPerformanceMinute.getAvgLoadRate())
                        .gt(BeltHealthConfig::getDeductionRangeMax, beltPerformanceMinute.getAvgLoadRate())
                        .eq(BeltHealthConfig::getDeductionCode, type)
                        .last("limit 1")
        );
    }

    // 设置缓存
    private void setCacheForAnomaly(BeltPerformanceMinute beltPerformanceMinute, String type) {
        String cacheKey = getCacheKey(type, beltPerformanceMinute);
        cacheService.setStringExpired(cacheKey, beltPerformanceMinute.getId().toString(), 60 * 10);
    }

    // 创建异常信息
    private BeltAnomalyInfo createBeltAnomalyInfo(BeltPerformanceMinute beltPerformanceMinute, BeltHealthConfig config) {
        BeltAnomalyInfo beltAnomalyInfo = new BeltAnomalyInfo()
                .setTaskId(beltPerformanceMinute.getTaskId())
                .setAnomalyTime(beltPerformanceMinute.getStartTime())
                .setRouteId(beltPerformanceMinute.getRouteId())
                .setTaskId(beltPerformanceMinute.getTaskId())
                .setTaskType(beltPerformanceMinute.getTaskType())
                .setResolved(0)
                .setAnomalyItem(config.getDeductionItem())
                .setAnomalyDesc("")
                .setSuggestion("")
                .setItemLevel(config.getAnomalyLevel());
        beltAnomalyInfo.setIsDeleted(0);
        return beltAnomalyInfo;
    }

    // 创建健康分数
    private BeltHealthScore createBeltHealthScore(BeltPerformanceMinute beltPerformanceMinute, BeltHealthConfig config) {
        BeltHealthScore beltHealthScore = new BeltHealthScore()
                .setDeductionItem(config.getDeductionItem())
                .setDeductionScore(config.getDeductionScore())
                .setHealthType(config.getHealthType())
                .setBeltId(beltPerformanceMinute.getBeltId())
                .setTaskId(beltPerformanceMinute.getTaskId())
                .setTaskType(beltPerformanceMinute.getTaskType())
                .setRouteId(beltPerformanceMinute.getRouteId())
                .setScoreTime(beltPerformanceMinute.getStartTime());
        beltHealthScore.setIsDeleted(0);
        return beltHealthScore;
    }

    // 获取缓存键
    private String getCacheKey(String type, BeltPerformanceMinute beltPerformanceMinute) {
        switch (type) {
            case LOAD_FACTOR_CODE:
                return "LOAD_FACTOR:" + beltPerformanceMinute.getId();
            case NO_LOAD_FACTOR_CODE:
                return "NO_LOAD_FACTOR:" + beltPerformanceMinute.getId();
            case Resistance_FACTOR_CODE:
                return "Resistance_FACTOR:" + beltPerformanceMinute.getId();
            default:
                throw new IllegalArgumentException("Invalid type: " + type);
        }
    }

    // 更新当天健康分数
    private void updateBeltHealthScoreDay(BeltHealthScore beltHealthScore, Long beltId) {
        beltHealthScoreDay.update(
                new LambdaUpdateWrapper<BeltHealthScoreDay>()
                        .set(BeltHealthScoreDay::getScore, beltHealthScore.getDeductionScore())
                        .eq(BeltHealthScoreDay::getBeltId, beltId)
                        .eq(BeltHealthScoreDay::getCreateTime, LocalDate.now().atStartOfDay())
        );
    }


}